package com.example.easyexcel.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.example.easyexcel.dao.AssetTargetInfoMapper;
import com.example.easyexcel.dto.AssetTargetInfo;
import com.example.easyexcel.listener.AssetTargetInfoListener;
import com.example.easyexcel.service.AssetTargetInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@RequiredArgsConstructor
@Slf4j
public class AssetTargetInfoServiceImpl implements AssetTargetInfoService {

    private final AssetTargetInfoMapper assetTargetInfoMapper;

    // 配置常量
    private static final int BATCH_SIZE = 5000;
    private static final int CORE_POOL_MULTIPLIER = 2;
    private static final int MAX_POOL_MULTIPLIER = 4;
    private static final int QUEUE_CAPACITY = 1000;
    private static final long RECORDS_PER_SHEET = 100000L; // 每个Sheet分配10万条记录的空间

    private final ThreadPoolExecutor sheetExecutor = createThreadPool();

    private ThreadPoolExecutor createThreadPool() {
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        int corePoolSize = availableProcessors * CORE_POOL_MULTIPLIER;
        int maxPoolSize = availableProcessors * MAX_POOL_MULTIPLIER;

        return new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadFactory() {
                    private final AtomicInteger threadNumber = new AtomicInteger(1);
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "excel-processor-" + threadNumber.getAndIncrement());
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    @Override
    public void importTargetExcel(MultipartFile file) {
        long totalStartTime = System.currentTimeMillis();
        Path tempFile = null;

        try {
            // 创建临时文件
            tempFile = Files.createTempFile("excel-import-", ".xlsx");
            file.transferTo(tempFile);
            log.info("临时文件创建成功: {}", tempFile.toString());

            // 获取sheet数量
            long sheetCountStartTime = System.currentTimeMillis();
            int sheetCount = getSheetCount(tempFile.toString());
            long sheetCountTime = System.currentTimeMillis() - sheetCountStartTime;
            log.info("获取sheet数量耗时: {} 毫秒, 共 {} 个sheet", sheetCountTime, sheetCount);

            // 处理所有sheet
            long processSheetsStartTime = System.currentTimeMillis();
            processAllSheets(tempFile.toString(), sheetCount);
            long processSheetsTime = System.currentTimeMillis() - processSheetsStartTime;
            log.info("处理所有sheet总耗时: {} 毫秒", processSheetsTime);

            long totalElapsedTime = System.currentTimeMillis() - totalStartTime;
            log.info("Excel导入完成总耗时: {} 毫秒", totalElapsedTime);

        } catch (Exception e) {
            long totalElapsedTime = System.currentTimeMillis() - totalStartTime;
            log.error("Excel导入失败，已耗时: {} 毫秒", totalElapsedTime, e);
            throw new RuntimeException("Excel导入失败: " + e.getMessage(), e);
        } finally {
            if (tempFile != null) {
                try {
                    Files.deleteIfExists(tempFile);
                    log.debug("临时文件已删除: {}", tempFile.toString());
                } catch (Exception e) {
                    log.warn("删除临时文件失败: {}", e.getMessage());
                }
            }
        }
    }

    private int getSheetCount(String filePath) {
        try (ExcelReader excelReader = EasyExcel.read(filePath).build()) {
            return excelReader.excelExecutor().sheetList().size();
        } catch (Exception e) {
            log.warn("获取sheet数量失败，默认使用1个sheet: {}", e.getMessage());
            return 1;
        }
    }

    /**
     * 处理所有sheet，为每个sheet分配独立的asset_no范围
     */
    private void processAllSheets(String filePath, int sheetCount) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        long processStartTime = System.currentTimeMillis();

        for (int i = 0; i < sheetCount; i++) {
            int sheetIndex = i;
            long sheetStartIndex = i * RECORDS_PER_SHEET; // 计算当前sheet的起始索引
            long sheetStartTime = System.currentTimeMillis();

            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try (InputStream inputStream = new FileInputStream(filePath)) {
                    // 为每个sheet创建独立的监听器实例，并传入sheet编号和起始索引
                    AssetTargetInfoListener listener = new AssetTargetInfoListener(
                            assetTargetInfoMapper, sheetIndex, sheetStartIndex);

                    EasyExcel.read(inputStream, AssetTargetInfo.class, listener)
                            .sheet(sheetIndex)
                            .doRead();

                    long sheetElapsedTime = System.currentTimeMillis() - sheetStartTime;
                    log.info("Sheet[{}] 导入完成，耗时: {} 毫秒", sheetIndex, sheetElapsedTime);
                } catch (Exception e) {
                    long sheetElapsedTime = System.currentTimeMillis() - sheetStartTime;
                    log.error("Sheet[{}] 导入失败，已耗时: {} 毫秒", sheetIndex, sheetElapsedTime, e);
                }
            }, sheetExecutor);

            futures.add(future);
        }

        // 等待所有sheet处理完成
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .get(30, TimeUnit.MINUTES);
            long processElapsedTime = System.currentTimeMillis() - processStartTime;
            log.info("所有sheet处理完成，总耗时: {} 毫秒", processElapsedTime);
        } catch (TimeoutException e) {
            log.error("处理Excel超时，请检查数据量或系统性能", e);
            throw new RuntimeException("处理超时", e);
        } catch (Exception e) {
            log.error("处理Excel时发生错误", e);
            throw new RuntimeException("处理失败", e);
        }
    }

    public void shutdownExecutors() {
        long shutdownStartTime = System.currentTimeMillis();
        log.info("开始关闭线程池...");

        sheetExecutor.shutdown();
        try {
            if (!sheetExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                List<Runnable> notExecutedTasks = sheetExecutor.shutdownNow();
                log.warn("线程池强制关闭，{}个任务未被执行", notExecutedTasks.size());
            }
            long shutdownTime = System.currentTimeMillis() - shutdownStartTime;
            log.info("线程池已关闭，耗时: {} 毫秒", shutdownTime);
        } catch (InterruptedException e) {
            sheetExecutor.shutdownNow();
            Thread.currentThread().interrupt();
            log.error("线程池关闭被中断", e);
        }
    }
}